package com.seanzx.resolver.impl;

import com.seanzx.common.Pair;
import com.seanzx.common.Result;
import com.seanzx.constant.SymbolTable;
import com.seanzx.dp.condition.ConditionNode;
import com.seanzx.dp.condition.ConditionTree;
import com.seanzx.dp.condition.ConditionType;
import com.seanzx.lexical.Token;
import com.seanzx.resolver.IResolver;
import com.seanzx.dp.ProcessorController;
import com.seanzx.common.Lists;
import com.seanzx.dp.entity.DataOperateEntity;

import java.util.ArrayList;
import java.util.List;

public class DeleteDataResolver implements IResolver<DataOperateEntity> {
    private final ProcessorController processorController = ProcessorController.get();

    @Override
    public boolean matches(List<Token> tokens) {
        return tokens.size() > 3 && SymbolTable.DELETE.equals(tokens.get(0).getValue()) && "FROM".equals(tokens.get(1).getValue());
    }

    @Override
    public DataOperateEntity translate(List<Token> tokens) {
        DataOperateEntity entity = new DataOperateEntity();
        int startIndex;
        if (tokens.get(3).getValue().equals(".")) {
            entity.setDatabaseName(tokens.get(2).getValue());
            entity.setTableName(tokens.get(4).getValue());
            startIndex = 5;
        } else {
            entity.setTableName(tokens.get(2).getValue());
            startIndex = 3;
        }
        if (tokens.size() <= startIndex) {
            return entity;
        }
        assert "WHERE".equals(tokens.get(startIndex).getValue());

        ConditionTree conditionTree = new ConditionTree(entity.getDatabaseName(), entity.getTableName());
        ConditionNode node = null;
        boolean isAnd = false, isOr = false;
        for (int i = startIndex + 1; i < tokens.size(); i++) {
            String value = tokens.get(i).getValue();
            // TODO: bracket
            switch(value) {
                case "AND":
                    isAnd = true;
                    isOr = false;
                    break;
                case "OR":
                    isOr = true;
                    isAnd = false;
                    break;
                default:
                    Pair<Integer, ConditionNode> pair = getNode(tokens, i);
                    i = pair.left();
                    if (node == null) {
                        node = pair.right();
                        conditionTree.add(node);
                    } else if (isAnd) {
                        node.and(pair.right());
                    } else if (isOr) {
                        ConditionNode rightNode = pair.right();
                        node.or(rightNode);
                        node = rightNode;
                    }
            }
        }
        entity.setConditionTree(conditionTree);
        return entity;
    }

    @Override
    public boolean validate(Object parameter) {
        return true;
    }

    private Pair<Integer, ConditionNode> getNode(List<Token> tokens, int index) {
        ConditionNode node = new ConditionNode(tokens.get(index).getValue());
        assert tokens.size() > index + 2;
        String conditionType = tokens.get(index + 1).getValue();
        if ("BETWEEN".equals(conditionType)) {
            node.setConditionType(ConditionType.of("BETWEEN AND"));
            assert tokens.size() > index + 4 && "AND".equals(tokens.get(index + 3).getValue());
            node.setValues(Lists.of(tokens.get(index + 2).getValue(), tokens.get(index + 4).getValue()));
            return Pair.of(index + 4, node);
        }
        if ("IN".equals(conditionType)) {
            node.setConditionType(ConditionType.of("IN"));
            List<String> valueList = new ArrayList<>();
            assert tokens.size() > index + 4 && "(".equals(tokens.get(index + 2).getValue());
            int i = index + 3;
            for (; i < tokens.size(); i++) {
                String value = tokens.get(i).getValue();
                if (")".equals(value)) {
                    break;
                }
                if (",".equals(value)) {
                    continue;
                }
                valueList.add(value);
            }
            node.setValues(valueList);
            return Pair.of(i, node);
        }
        node.setConditionType(ConditionType.of(conditionType));
        node.setValues(Lists.of(tokens.get(index + 2).getValue()));
        return Pair.of(index + 2, node);
    }

    @Override
    public Result access(Object parameter) {
        return processorController.delete((DataOperateEntity) parameter);
    }
}
